﻿using JustMathCore.FilePool;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JMCMD
{
    public class GenAddr
    {
        static JustMathCore.RocksDB.DB db;
        static string logfile;
        static bool exit;
        class checktask
        {
            public JustMathCore.Hex160 hash;
            //public byte[] prikey;
            public string wif;
            public string addr;
            public UInt32 seed;
        }
        static System.Collections.Concurrent.ConcurrentQueue<checktask> queues;
        public static void DoGen(string dbpath, JustMathCore.AddrTools.AddrType addrType, Roll.RollType rantype, byte rantoken, string outpath, uint begin, uint end)
        {


            //get conv
            var conv = JustMathCore.AddrTools.GetConv(addrType);

            //getrantype
            var roll = Roll.RoolMgr.GetRool(rantype, rantoken);
            //load bf
            JustMathCore.BitArray160 bitfilter = JustMathCore.BitArray160.Load(dbpath);

            if (System.IO.Directory.Exists(outpath) == false)
                System.IO.Directory.CreateDirectory(outpath);
            logfile = System.IO.Path.Combine(outpath, "log_" + DateTime.Now.ToFileTimeUtc() + ".txt");




            queues = new System.Collections.Concurrent.ConcurrentQueue<checktask>();
            exit = false;
            System.Threading.ThreadPool.QueueUserWorkItem((s) =>
            {
                db = new JustMathCore.RocksDB.DB();
                db.Open(dbpath, false);
                while (!exit)
                {
                    if (queues.TryDequeue(out checktask t))
                    {
                        if (t.seed == 0x000DB54F)
                        {

                        }
                        var type = db.Get(t.hash);
                        if (type != null)
                        {
                            var strtype = System.Text.Encoding.ASCII.GetString(type);
                            Console.WriteLine("real got:" + t.addr + "  " + strtype);
                            System.IO.File.AppendAllText(logfile,
                                t.addr + "  " + strtype + "  *" + t.seed.ToString("X08") + "*  " + t.wif + "\n");
                        }
                    }
                }
                db.Dispose();
            });

            PForGen(begin, end, bitfilter, roll, conv);
            exit = true;


        }
        static void QueueDBCheck(JustMathCore.Hex160 hash, string wif, string addr, UInt32 seed)
        {
            queues.Enqueue(new checktask() { hash = hash, addr = addr, seed = seed, wif = wif });

        }
        public static void PForGen(UInt32 begin, UInt32 endwith, JustMathCore.BitArray160 bitfilter, Roll.RoolMgr.IRoll roll, JustMathCore.AddrTools.IAddrConv conv)
        {
            int thread = 8;
            uint seedblockbegin = begin / 0x10000;//6w个种子空间为一个
            uint seedblockendwith = endwith / 0x10000;

            for (uint blockid = seedblockbegin; blockid <= seedblockendwith; blockid++)
            {
                var logline = "start block:" + conv.GetType().Name + " " + roll.type + ":" + roll.turn.ToString("X02") + "=" + blockid.ToString("X04") + " " + DateTime.Now.ToLongTimeString().ToString();
                System.IO.File.WriteAllText("laststate.txt", logline);
                Console.WriteLine(logline);

                uint seedbegin = blockid * 0x10000;
                uint seedend = seedbegin + 0xffff;
                System.Threading.Tasks.Parallel.For(0, thread, (tid) =>
                  {
                      byte[] prikey = new byte[32];
                      byte[] rprikey = new byte[32];
                      for (long seed = seedbegin + tid; seed <= seedend; seed += thread)
                      {
                          roll.Roll32((uint)seed, prikey);
                          for (var i = 0; i < 32; i++)
                          {
                              rprikey[i] = prikey[31 - i];
                          }
                          //test random
                          {
                              byte[] line = new byte[32];
                              JustMathCore.Hex160 key = null;
                              JustMathCore.Hex160 keyr = null;
                              try
                              {
                                  key = conv.GetHashAddr(prikey);
                              }
                              catch (Exception err)
                              {
                                  Console.WriteLine("error key:" + seed);
                              }
                              try
                              {
                                  keyr = conv.GetHashAddr(rprikey);
                              }
                              catch (Exception err)
                              {
                                  Console.WriteLine("error reverse key:" + seed);
                              }

                              if (!System.Object.ReferenceEquals(null, key) && bitfilter.Exist(key))
                              {
                                  var addr = conv.GetAddr(prikey);

                                  Console.WriteLine("maybe got key:" + seed.ToString("X08") + ":" + addr + " priv:" + BitConverter.ToUInt32(prikey).ToString("x08"));
                                  QueueDBCheck(key, conv.GetWif(prikey), addr, (uint)seed);
                              }
                              if (!System.Object.ReferenceEquals(null, keyr) && bitfilter.Exist(keyr))
                              {
                                  var addr = conv.GetAddr(rprikey);
                                  Console.WriteLine("maybe got key:" + seed.ToString("X08") + ":" + addr + "(reverse)");
                                  QueueDBCheck(keyr, conv.GetWif(rprikey), addr, (uint)seed);
                              }

                          }

                      }
                  });
            }
        }

        public static void DoMLTest(string dbpath, JustMathCore.AddrTools.AddrType addrType, string outpath)
        {
            //get conv
            var conv = JustMathCore.AddrTools.GetConv(addrType);
            //load bf
            JustMathCore.BitArray160 bitfilter = JustMathCore.BitArray160.Load(dbpath);

            if (System.IO.Directory.Exists(outpath) == false)
                System.IO.Directory.CreateDirectory(outpath);
            logfile = System.IO.Path.Combine(outpath, "log_" + DateTime.Now.ToFileTimeUtc() + ".txt");


            queues = new System.Collections.Concurrent.ConcurrentQueue<checktask>();
            exit = false;
            System.Threading.ThreadPool.QueueUserWorkItem((s) =>
            {
                db = new JustMathCore.RocksDB.DB();
                db.Open(dbpath, false);
                while (!exit)
                {
                    if (queues.TryDequeue(out checktask t))
                    {
                        if (t.seed == 0x000DB54F)
                        {

                        }
                        var type = db.Get(t.hash);
                        if (type != null)
                        {
                            var strtype = System.Text.Encoding.ASCII.GetString(type);
                            Console.WriteLine("real got:" + t.addr + "  " + strtype);
                            System.IO.File.AppendAllText(logfile,
                                t.addr + "  " + strtype + "  *" + t.seed.ToString("X08") + "*  " + t.wif + "\n");
                        }
                    }
                }
                db.Dispose();
            });

            PForGenML(logfile, outpath, bitfilter, conv);
            exit = true;
        }
        static void PForGenML(string logfile, string outpath, JustMathCore.BitArray160 bitfilter, JustMathCore.AddrTools.IAddrConv conv)
        {
            //加載或者初始化訓練
            JustMathCore.ML.IMLModel ml = new JustMathCore.ML.ConvNetModel();

            var modelfile = System.IO.Path.Combine(outpath, "ml.ml");
            bool init = false;
            if (System.IO.File.Exists(modelfile))
            {
                init = ml.LoadModel(modelfile);
            }
            if (init)
            {
                Console.WriteLine("ML 已初始化");
            }
            else
            {
                Console.WriteLine("ML 開始初始化，比較久");
                ml.InitModel();
                var ran = new Random();
                var roll = Roll.RoolMgr.GetRool(Roll.RollType.CS, 0);
                var pkey = new byte[32];
                for (var i = 0; i < 100; i++)
                {
                    roll.Roll32((uint)ran.Next(), pkey);
                    var realaddr = conv.GetHashAddr(pkey);
                    ml.Train(realaddr, pkey);
                }
                ml.SaveModel(modelfile);
                Console.WriteLine("ML 初始化完畢");
            }
            //init or load model

            while (true)
            {
                Console.WriteLine("Try ML rool. never stop.");
                var top100 = JustMathCore.AddrToolCoin.BTCTop.GetTop100(conv.Type);
                for (var i = 0; i < top100.Length; i++)
                {
                    //gotaddr
                    var hash = conv.Addr2Hash(top100[i]);
                    byte[] pkey = new byte[32];
                    ml.Predict(hash, pkey);

                    byte[] rprikey = new byte[32];
                    for (var _i = 0; _i < 32; _i++)
                    {
                        rprikey[_i] = pkey[31 - _i];
                    }
                    {//正
                        var addrgot = conv.GetAddr(pkey);
                        if (addrgot == top100[i])
                        {
                            Console.WriteLine("lucky " + conv.GetWif(pkey));
                            System.IO.File.AppendAllText(logfile,
    addrgot + "  " + "*  " + conv.GetWif(pkey) + "\n");
                        }
                        var addrgothash = conv.GetHashAddr(pkey);
                        if (bitfilter.Exist(pkey))
                        {
                            //find a maybe result;
                            var addr = conv.GetAddr(pkey);
                            QueueDBCheck(pkey, conv.GetWif(pkey), addr, (uint)0);
                            Console.WriteLine("maybe result." + addrgot);
                        }
                        else
                        {
                            ml.Train(addrgothash, pkey);
                        }
                    }
                    {//逆
                        var addrgot = conv.GetAddr(rprikey);
                        if (addrgot == top100[i])
                        {
                            Console.WriteLine("lucky " + conv.GetWif(pkey));
                            System.IO.File.AppendAllText(logfile,
    addrgot + "  " + "*  " + conv.GetWif(rprikey) + "\n");
                        }

                        var addrgothash = conv.GetHashAddr(rprikey);
                        if (bitfilter.Exist(rprikey))
                        {
                            //find a maybe result;
                            var addr = conv.GetAddr(rprikey);
                            QueueDBCheck(rprikey, conv.GetWif(rprikey), addr, (uint)0);
                            Console.WriteLine("maybe result." + addrgot);
                        }
                        else
                        {
                            ml.Train(addrgothash, rprikey);
                        }
                    }
                }
                ml.SaveModel(modelfile);
            }
        }
    }
}
